#include <net.h>
#include <os/string.h>
#include <screen.h>
#include <emacps/xemacps_example.h>
#include <emacps/xemacps.h>

#include <os/sched.h>
#include <os/mm.h>
#include <io.h>

EthernetFrame rx_buffers[RXBD_CNT];

EthernetFrame tx_buffer;
uint32_t rx_len[RXBD_CNT];
extern XEmacPs EmacPsInstance;
int net_poll_mode;
extern uint64_t txbd[];
volatile int rx_curr = 0, rx_tail = 0;
extern int rxbd_tail;
#define MAX_RECV_CNT 32
EthernetFrame recv_buffer[MAX_RECV_CNT];
size_t recv_length[MAX_RECV_CNT];
long do_net_recv(uintptr_t addr, size_t length, int num_packet, size_t* frLength)
{
    // TODO: 
    // receive packet by calling network driver's function
    // wait until you receive enough packets(`num_packet`).
    // maybe you need to call drivers' receive function multiple times ?
    long ret;
    uint64_t kva=walkaddr(addr,current_running->pgtable)+addr%(1<<12);
    uint64_t pa=kva2pa(kva);
    //printk("pa:0x%x\n\r",pa);
    uint64_t status0,status1;
    rxbd_tail=0;
    //for(int i=0;i<num_packet;i++){
        //rxbd_tail=0;
        status0=EmacPsRecv(&EmacPsInstance,(EthernetFrame *)pa,num_packet);
        if(status0!=XST_SUCCESS)
            printk("RECV FAILED!!!\n\r");
        status1=EmacPsWaitRecv(&EmacPsInstance,num_packet,frLength);
        if(status1!=XST_SUCCESS)
            printk("WAIT FOR RECV FAILED!!!\n\r");
        /*printk("packet %d: packet length: %d\n\r", i);
        printk("packet length: %d\n\r",recv_length[i]);
        int j=0;
        char *curr=(char *)&(recv_buffer[i]);
        //bu_count = (recv_length[i]+15)/16;
        for (j = 0; j<5; ++j) {
            //printk("j:%d upper:%d\n\r",j,bu_count);
            for (int k = 0; k < 16 && (j * 16 + k < recv_length[0]); ++k) {
                //printk("k:%d\n\r",k);
                printk("%02x ", (uint32_t)(*(uint8_t*)curr));
                ++curr;
            }
            printk("\n\r");
        }*/
    //}
    
    //else
    //    printk("SEND SUCCEEDED!!\n\r");
    //return;
    ret = rxbd_tail;
    return ret;
}

void do_net_send(uintptr_t addr, size_t length)
{
    // send all packet
    // maybe you need to call drivers' send function multiple times ? no
    uint64_t kva=walkaddr(addr,current_running->pgtable)+addr%(1<<12);
    uint64_t pa=kva2pa(kva);
    uint64_t status0,status1;
    status0=EmacPsSend(&EmacPsInstance,(EthernetFrame *)pa,length);
    uint32_t txqbase=XEmacPs_ReadReg(EmacPsInstance.Config.BaseAddress,XEMACPS_TXQBASE_OFFSET);
    uint64_t current_txbd=*(uint64_t *)pa2kva(txqbase);
    status1=EmacPsWaitSend(&EmacPsInstance);
    return;
}

void do_net_irq_mode(int mode)
{
    // TODO:
    // turn on/off network driver's interrupt mode
    //printk("set mode %d\n\r",mode);
    if(mode)
        XEmacPs_WriteReg(EmacPsInstance.Config.BaseAddress,XEMACPS_IER_OFFSET,XEMACPS_IXR_ALL_MASK );
    else
        XEmacPs_WriteReg(EmacPsInstance.Config.BaseAddress,XEMACPS_IDR_OFFSET,XEMACPS_IXR_ALL_MASK);
        //*((uint32_t*)EmacPsInstance.Config.BaseAddress+XEMACPS_IDR_OFFSET)=XEMACPS_IXR_ALL_MASK;
        //writel(XEMACPS_IXR_ALL_MASK,(volatile void *)EmacPsInstance.Config.BaseAddress+XEMACPS_IDR_OFFSET);
    //printk("set mode 0x%x done\n\r",mode);
}

long int atol(const char* str)
{
    int base = 10;
    if ((str[0] == '0' && str[1] == 'x') ||
        (str[0] == '0' && str[1] == 'X')) {
        base = 16;
        str += 2;
    }
    long ret = 0;
    while (*str != '\0') {
        if ('0' <= *str && *str <= '9') {
            ret = ret * base + (*str - '0');
        } else if (base == 16) {
            if ('a' <= *str && *str <= 'f'){
                ret = ret * base + (*str - 'a'+10);
            } else if ('A' <= *str && *str <= 'F') {
                ret = ret * base + (*str - 'A'+10);
            } else {
                return 0;
            }
        } else {
            return 0;
        }
        ++str;
    }
    return ret;
}

int bu_count;
int recv_s(int argc, char *argv[])
{
    int mode = 0;
    int size = 1;
    //printk("[RECV TASK] start recv(%d):                    ", size);
    if(argc > 1) {
        size = atol(argv[1]);
        printk("%d \n", size);
    }
    if(argc > 2) {        
        if (strcmp(argv[2], "1") == 0) {
            mode = 1;
        }
    }
    do_net_irq_mode(mode);

    vt100_move_cursor(1, 1);
    printk("[RECV TASK] start recv(%d):                    ", size);

    int ret = do_net_recv(recv_buffer, size * sizeof(EthernetFrame), size, recv_length);
    //printk("%d\n", ret);
    char *curr = (char *)recv_buffer;
    /*for (int i = 0; i < size; ++i) {
        printk("packet %d:\n\r", i);
        printk("packet length: %d\n\r",recv_length[i]);
        int j=0;
        curr=(char *)&(recv_buffer[i]);
        bu_count = (recv_length[i]+15)/16;
        for (j = 0; j<5; ++j) {
            //printk("j:%d upper:%d\n\r",j,bu_count);
            for (int k = 0; k < 16 && (j * 16 + k < recv_length[i]); ++k) {
                //printk("k:%d\n\r",k);
                printk("%02x ", (uint32_t)(*(uint8_t*)curr));
                ++curr;
            }
            printk("\n\r");
        }
    }*/

    return 0;
}
